/*
* Copyright (C) 1997-2001 Id Software, Inc.
* 
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
* 
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE.
* 
* See the GNU General Public License for more details.
* 
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place - Suite 330, Boston, MA 02111-1307, USA.
*  
*/

// Created on 17.01.2004 by RST.
// $Id: SV_USER.java,v 1.10 2005/12/17 20:32:01 salomo Exp $
using System;
using Defines = Suake2.UI.Defines;
using Globals = Suake2.UI.Globals;
using Cmd = Suake2.UI.game.Cmd;
using GameAI = Suake2.UI.game.GameAI;
using GameBase = Suake2.UI.game.GameBase;
using Info = Suake2.UI.game.Info;
using PlayerClient = Suake2.UI.game.PlayerClient;
using edict_t = Suake2.UI.game.edict_t;
using entity_state_t = Suake2.UI.game.entity_state_t;
using usercmd_t = Suake2.UI.game.usercmd_t;
using Cbuf = Suake2.UI.qcommon.Cbuf;
using Com = Suake2.UI.qcommon.Com;
using Cvar = Suake2.UI.qcommon.Cvar;
using FS = Suake2.UI.qcommon.FS;
using MSG = Suake2.UI.qcommon.MSG;
using SZ = Suake2.UI.qcommon.SZ;
using Lib = Suake2.UI.util.Lib;
namespace Suake2.UI.server
{
	
	public class SV_USER
	{
		//UPGRADE_NOTE: Field 'EnclosingInstance' was added to class 'AnonymousClassRunnable' to access its enclosing instance. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1019'"
		internal class AnonymousClassRunnable : IThreadRunnable
		{
			public AnonymousClassRunnable(SV_USER enclosingInstance)
			{
				InitBlock(enclosingInstance);
			}
			private void  InitBlock(SV_USER enclosingInstance)
			{
				this.enclosingInstance = enclosingInstance;
			}
			private SV_USER enclosingInstance;
			public SV_USER Enclosing_Instance
			{
				get
				{
					return enclosingInstance;
				}
				
			}
			public virtual void  Run()
			{
				SV_USER.SV_New_f();
			}
		}
		//UPGRADE_NOTE: Field 'EnclosingInstance' was added to class 'AnonymousClassRunnable1' to access its enclosing instance. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1019'"
		internal class AnonymousClassRunnable1 : IThreadRunnable
		{
			public AnonymousClassRunnable1(SV_USER enclosingInstance)
			{
				InitBlock(enclosingInstance);
			}
			private void  InitBlock(SV_USER enclosingInstance)
			{
				this.enclosingInstance = enclosingInstance;
			}
			private SV_USER enclosingInstance;
			public SV_USER Enclosing_Instance
			{
				get
				{
					return enclosingInstance;
				}
				
			}
			public virtual void  Run()
			{
				SV_USER.SV_New_f();
			}
		}
		//UPGRADE_NOTE: Field 'EnclosingInstance' was added to class 'AnonymousClassRunnable2' to access its enclosing instance. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1019'"
		internal class AnonymousClassRunnable2 : IThreadRunnable
		{
			public AnonymousClassRunnable2(SV_USER enclosingInstance)
			{
				InitBlock(enclosingInstance);
			}
			private void  InitBlock(SV_USER enclosingInstance)
			{
				this.enclosingInstance = enclosingInstance;
			}
			private SV_USER enclosingInstance;
			public SV_USER Enclosing_Instance
			{
				get
				{
					return enclosingInstance;
				}
				
			}
			public virtual void  Run()
			{
				SV_USER.SV_Configstrings_f();
			}
		}
		//UPGRADE_NOTE: Field 'EnclosingInstance' was added to class 'AnonymousClassRunnable3' to access its enclosing instance. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1019'"
		internal class AnonymousClassRunnable3 : IThreadRunnable
		{
			public AnonymousClassRunnable3(SV_USER enclosingInstance)
			{
				InitBlock(enclosingInstance);
			}
			private void  InitBlock(SV_USER enclosingInstance)
			{
				this.enclosingInstance = enclosingInstance;
			}
			private SV_USER enclosingInstance;
			public SV_USER Enclosing_Instance
			{
				get
				{
					return enclosingInstance;
				}
				
			}
			public virtual void  Run()
			{
				SV_USER.SV_Baselines_f();
			}
		}
		//UPGRADE_NOTE: Field 'EnclosingInstance' was added to class 'AnonymousClassRunnable4' to access its enclosing instance. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1019'"
		internal class AnonymousClassRunnable4 : IThreadRunnable
		{
			public AnonymousClassRunnable4(SV_USER enclosingInstance)
			{
				InitBlock(enclosingInstance);
			}
			private void  InitBlock(SV_USER enclosingInstance)
			{
				this.enclosingInstance = enclosingInstance;
			}
			private SV_USER enclosingInstance;
			public SV_USER Enclosing_Instance
			{
				get
				{
					return enclosingInstance;
				}
				
			}
			public virtual void  Run()
			{
				SV_USER.SV_Begin_f();
			}
		}
		//UPGRADE_NOTE: Field 'EnclosingInstance' was added to class 'AnonymousClassRunnable5' to access its enclosing instance. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1019'"
		internal class AnonymousClassRunnable5 : IThreadRunnable
		{
			public AnonymousClassRunnable5(SV_USER enclosingInstance)
			{
				InitBlock(enclosingInstance);
			}
			private void  InitBlock(SV_USER enclosingInstance)
			{
				this.enclosingInstance = enclosingInstance;
			}
			private SV_USER enclosingInstance;
			public SV_USER Enclosing_Instance
			{
				get
				{
					return enclosingInstance;
				}
				
			}
			public virtual void  Run()
			{
				SV_USER.SV_Nextserver_f();
			}
		}
		//UPGRADE_NOTE: Field 'EnclosingInstance' was added to class 'AnonymousClassRunnable6' to access its enclosing instance. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1019'"
		internal class AnonymousClassRunnable6 : IThreadRunnable
		{
			public AnonymousClassRunnable6(SV_USER enclosingInstance)
			{
				InitBlock(enclosingInstance);
			}
			private void  InitBlock(SV_USER enclosingInstance)
			{
				this.enclosingInstance = enclosingInstance;
			}
			private SV_USER enclosingInstance;
			public SV_USER Enclosing_Instance
			{
				get
				{
					return enclosingInstance;
				}
				
			}
			public virtual void  Run()
			{
				SV_USER.SV_Disconnect_f();
			}
		}
		//UPGRADE_NOTE: Field 'EnclosingInstance' was added to class 'AnonymousClassRunnable7' to access its enclosing instance. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1019'"
		internal class AnonymousClassRunnable7 : IThreadRunnable
		{
			public AnonymousClassRunnable7(SV_USER enclosingInstance)
			{
				InitBlock(enclosingInstance);
			}
			private void  InitBlock(SV_USER enclosingInstance)
			{
				this.enclosingInstance = enclosingInstance;
			}
			private SV_USER enclosingInstance;
			public SV_USER Enclosing_Instance
			{
				get
				{
					return enclosingInstance;
				}
				
			}
			public virtual void  Run()
			{
				SV_USER.SV_ShowServerinfo_f();
			}
		}
		//UPGRADE_NOTE: Field 'EnclosingInstance' was added to class 'AnonymousClassRunnable8' to access its enclosing instance. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1019'"
		internal class AnonymousClassRunnable8 : IThreadRunnable
		{
			public AnonymousClassRunnable8(SV_USER enclosingInstance)
			{
				InitBlock(enclosingInstance);
			}
			private void  InitBlock(SV_USER enclosingInstance)
			{
				this.enclosingInstance = enclosingInstance;
			}
			private SV_USER enclosingInstance;
			public SV_USER Enclosing_Instance
			{
				get
				{
					return enclosingInstance;
				}
				
			}
			public virtual void  Run()
			{
				SV_USER.SV_BeginDownload_f();
			}
		}
		//UPGRADE_NOTE: Field 'EnclosingInstance' was added to class 'AnonymousClassRunnable9' to access its enclosing instance. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1019'"
		internal class AnonymousClassRunnable9 : IThreadRunnable
		{
			public AnonymousClassRunnable9(SV_USER enclosingInstance)
			{
				InitBlock(enclosingInstance);
			}
			private void  InitBlock(SV_USER enclosingInstance)
			{
				this.enclosingInstance = enclosingInstance;
			}
			private SV_USER enclosingInstance;
			public SV_USER Enclosing_Instance
			{
				get
				{
					return enclosingInstance;
				}
				
			}
			public virtual void  Run()
			{
				SV_USER.SV_NextDownload_f();
			}
		}
		
		internal static edict_t sv_player;
		
		public class ucmd_t
		{
			public ucmd_t(System.String n, IThreadRunnable r)
			{
				name = n;
				this.r = r;
			}
			
			internal System.String name;
			
			internal IThreadRunnable r;
		}
		
		//UPGRADE_NOTE: The initialization of  'u1' was moved to static method 'jake2.server.SV_USER'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		internal static ucmd_t u1;
		
		//UPGRADE_NOTE: The initialization of  'ucmds' was moved to static method 'jake2.server.SV_USER'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		internal static ucmd_t[] ucmds;
		
		public const int MAX_STRINGCMDS = 8;
		
		/*
		* ============================================================
		* 
		* USER STRINGCMD EXECUTION
		* 
		* sv_client and sv_player will be valid.
		* ============================================================
		*/
		
		/*
		* ================== SV_BeginDemoServer ==================
		*/
		public static void  SV_BeginDemoserver()
		{
			System.String name;
			
			name = "demos/" + SV_INIT.sv.name;
			try
			{
				SV_INIT.sv.demofile = FS.FOpenFile(name);
			}
			catch (System.IO.IOException e)
			{
				Com.Error(Defines.ERR_DROP, "Couldn't open " + name + "\n");
			}
			if (SV_INIT.sv.demofile == null)
				Com.Error(Defines.ERR_DROP, "Couldn't open " + name + "\n");
		}
		
		/*
		* ================ SV_New_f
		* 
		* Sends the first message from the server to a connected client. This will
		* be sent on the initial connection and upon each server load.
		* ================
		*/
		public static void  SV_New_f()
		{
			System.String gamedir;
			int playernum;
			edict_t ent;
			
			Com.DPrintf("New() from " + SV_MAIN.sv_client.name + "\n");
			
			if (SV_MAIN.sv_client.state != Defines.cs_connected)
			{
				Com.Printf("New not valid -- already spawned\n");
				return ;
			}
			
			// demo servers just dump the file message
			if (SV_INIT.sv.state == Defines.ss_demo)
			{
				SV_BeginDemoserver();
				return ;
			}
			
			//
			// serverdata needs to go over for all types of servers
			// to make sure the protocol is right, and to set the gamedir
			//
			gamedir = Cvar.VariableString("gamedir");
			
			// send the serverdata
			MSG.WriteByte(SV_MAIN.sv_client.netchan.message, Defines.svc_serverdata);
			MSG.WriteInt(SV_MAIN.sv_client.netchan.message, Defines.PROTOCOL_VERSION);
			
			MSG.WriteLong(SV_MAIN.sv_client.netchan.message, SV_INIT.svs.spawncount);
			MSG.WriteByte(SV_MAIN.sv_client.netchan.message, SV_INIT.sv.attractloop?1:0);
			MSG.WriteString(SV_MAIN.sv_client.netchan.message, gamedir);
			
			if (SV_INIT.sv.state == Defines.ss_cinematic || SV_INIT.sv.state == Defines.ss_pic)
				playernum = - 1;
			//playernum = sv_client - svs.clients;
			else
				playernum = SV_MAIN.sv_client.serverindex;
			
			MSG.WriteShort(SV_MAIN.sv_client.netchan.message, playernum);
			
			// send full levelname
			MSG.WriteString(SV_MAIN.sv_client.netchan.message, SV_INIT.sv.configstrings[Defines.CS_NAME]);
			
			//
			// game server
			// 
			if (SV_INIT.sv.state == Defines.ss_game)
			{
				// set up the entity for the client
				ent = GameBase.g_edicts[playernum + 1];
				ent.s.number = playernum + 1;
				SV_MAIN.sv_client.edict = ent;
				SV_MAIN.sv_client.lastcmd = new usercmd_t();
				
				// begin fetching configstrings
				MSG.WriteByte(SV_MAIN.sv_client.netchan.message, Defines.svc_stufftext);
				MSG.WriteString(SV_MAIN.sv_client.netchan.message, "cmd configstrings " + SV_INIT.svs.spawncount + " 0\n");
			}
		}
		
		/*
		* ================== SV_Configstrings_f ==================
		*/
		public static void  SV_Configstrings_f()
		{
			int start;
			
			Com.DPrintf("Configstrings() from " + SV_MAIN.sv_client.name + "\n");
			
			if (SV_MAIN.sv_client.state != Defines.cs_connected)
			{
				Com.Printf("configstrings not valid -- already spawned\n");
				return ;
			}
			
			// handle the case of a level changing while a client was connecting
			if (Lib.atoi(Cmd.Argv(1)) != SV_INIT.svs.spawncount)
			{
				Com.Printf("SV_Configstrings_f from different level\n");
				SV_New_f();
				return ;
			}
			
			start = Lib.atoi(Cmd.Argv(2));
			
			// write a packet full of data
			
			while (SV_MAIN.sv_client.netchan.message.cursize < Defines.MAX_MSGLEN / 2 && start < Defines.MAX_CONFIGSTRINGS)
			{
				if (SV_INIT.sv.configstrings[start] != null && SV_INIT.sv.configstrings[start].Length != 0)
				{
					MSG.WriteByte(SV_MAIN.sv_client.netchan.message, Defines.svc_configstring);
					MSG.WriteShort(SV_MAIN.sv_client.netchan.message, start);
					MSG.WriteString(SV_MAIN.sv_client.netchan.message, SV_INIT.sv.configstrings[start]);
				}
				start++;
			}
			
			// send next command
			
			if (start == Defines.MAX_CONFIGSTRINGS)
			{
				MSG.WriteByte(SV_MAIN.sv_client.netchan.message, Defines.svc_stufftext);
				MSG.WriteString(SV_MAIN.sv_client.netchan.message, "cmd baselines " + SV_INIT.svs.spawncount + " 0\n");
			}
			else
			{
				MSG.WriteByte(SV_MAIN.sv_client.netchan.message, Defines.svc_stufftext);
				MSG.WriteString(SV_MAIN.sv_client.netchan.message, "cmd configstrings " + SV_INIT.svs.spawncount + " " + start + "\n");
			}
		}
		
		/*
		* ================== SV_Baselines_f ==================
		*/
		public static void  SV_Baselines_f()
		{
			int start;
			entity_state_t nullstate;
			entity_state_t base_Renamed;
			
			Com.DPrintf("Baselines() from " + SV_MAIN.sv_client.name + "\n");
			
			if (SV_MAIN.sv_client.state != Defines.cs_connected)
			{
				Com.Printf("baselines not valid -- already spawned\n");
				return ;
			}
			
			// handle the case of a level changing while a client was connecting
			if (Lib.atoi(Cmd.Argv(1)) != SV_INIT.svs.spawncount)
			{
				Com.Printf("SV_Baselines_f from different level\n");
				SV_New_f();
				return ;
			}
			
			start = Lib.atoi(Cmd.Argv(2));
			
			//memset (&nullstate, 0, sizeof(nullstate));
			nullstate = new entity_state_t(null);
			
			// write a packet full of data
			
			while (SV_MAIN.sv_client.netchan.message.cursize < Defines.MAX_MSGLEN / 2 && start < Defines.MAX_EDICTS)
			{
				base_Renamed = SV_INIT.sv.baselines[start];
				if (base_Renamed.modelindex != 0 || base_Renamed.sound != 0 || base_Renamed.effects != 0)
				{
					MSG.WriteByte(SV_MAIN.sv_client.netchan.message, Defines.svc_spawnbaseline);
					MSG.WriteDeltaEntity(nullstate, base_Renamed, SV_MAIN.sv_client.netchan.message, true, true);
				}
				start++;
			}
			
			// send next command
			
			if (start == Defines.MAX_EDICTS)
			{
				MSG.WriteByte(SV_MAIN.sv_client.netchan.message, Defines.svc_stufftext);
				MSG.WriteString(SV_MAIN.sv_client.netchan.message, "precache " + SV_INIT.svs.spawncount + "\n");
			}
			else
			{
				MSG.WriteByte(SV_MAIN.sv_client.netchan.message, Defines.svc_stufftext);
				MSG.WriteString(SV_MAIN.sv_client.netchan.message, "cmd baselines " + SV_INIT.svs.spawncount + " " + start + "\n");
			}
		}
		
		/*
		* ================== SV_Begin_f ==================
		*/
		public static void  SV_Begin_f()
		{
			Com.DPrintf("Begin() from " + SV_MAIN.sv_client.name + "\n");
			
			// handle the case of a level changing while a client was connecting
			if (Lib.atoi(Cmd.Argv(1)) != SV_INIT.svs.spawncount)
			{
				Com.Printf("SV_Begin_f from different level\n");
				SV_New_f();
				return ;
			}
			
			SV_MAIN.sv_client.state = Defines.cs_spawned;
			
			// call the game begin function
			PlayerClient.ClientBegin(SV_USER.sv_player);
			
			Cbuf.InsertFromDefer();
		}
		
		//=============================================================================
		
		/*
		* ================== SV_NextDownload_f ==================
		*/
		public static void  SV_NextDownload_f()
		{
			int r;
			int percent;
			int size;
			
			if (SV_MAIN.sv_client.download == null)
				return ;
			
			r = SV_MAIN.sv_client.downloadsize - SV_MAIN.sv_client.downloadcount;
			if (r > 1024)
				r = 1024;
			
			MSG.WriteByte(SV_MAIN.sv_client.netchan.message, Defines.svc_download);
			MSG.WriteShort(SV_MAIN.sv_client.netchan.message, r);
			
			SV_MAIN.sv_client.downloadcount += r;
			size = SV_MAIN.sv_client.downloadsize;
			if (size == 0)
				size = 1;
			percent = SV_MAIN.sv_client.downloadcount * 100 / size;
			MSG.WriteByte(SV_MAIN.sv_client.netchan.message, percent);
			SZ.Write(SV_MAIN.sv_client.netchan.message, SV_MAIN.sv_client.download, SV_MAIN.sv_client.downloadcount - r, r);
			
			if (SV_MAIN.sv_client.downloadcount != SV_MAIN.sv_client.downloadsize)
				return ;
			
			FS.FreeFile(SV_MAIN.sv_client.download);
			SV_MAIN.sv_client.download = null;
		}
		
		/*
		* ================== SV_BeginDownload_f ==================
		*/
		public static void  SV_BeginDownload_f()
		{
			System.String name;
			int offset = 0;
			
			name = Cmd.Argv(1);
			
			if (Cmd.Argc() > 2)
				offset = Lib.atoi(Cmd.Argv(2)); // downloaded offset
			
			// hacked by zoid to allow more conrol over download
			// first off, no .. or global allow check
			
			if (name.IndexOf("..") != - 1 || SV_MAIN.allow_download.value_Renamed == 0 || name[0] == '.' || name[0] == '/' || (name.StartsWith("players/") && 0 == SV_MAIN.allow_download_players.value_Renamed) || (name.StartsWith("models/") && 0 == SV_MAIN.allow_download_models.value_Renamed) || (name.StartsWith("sound/") && 0 == SV_MAIN.allow_download_sounds.value_Renamed) || (name.StartsWith("maps/") && 0 == SV_MAIN.allow_download_maps.value_Renamed) || name.IndexOf('/') == - 1)
			{
				// don't allow anything with ..
				// path
				MSG.WriteByte(SV_MAIN.sv_client.netchan.message, Defines.svc_download);
				MSG.WriteShort(SV_MAIN.sv_client.netchan.message, - 1);
				MSG.WriteByte(SV_MAIN.sv_client.netchan.message, 0);
				return ;
			}
			
			if (SV_MAIN.sv_client.download != null)
				FS.FreeFile(SV_MAIN.sv_client.download);
			
			SV_MAIN.sv_client.download = FS.LoadFile(name);
			
			// rst: this handles loading errors, no message yet visible 
			if (SV_MAIN.sv_client.download == null)
			{
				return ;
			}
			
			SV_MAIN.sv_client.downloadsize = SV_MAIN.sv_client.download.Length;
			SV_MAIN.sv_client.downloadcount = offset;
			
			if (offset > SV_MAIN.sv_client.downloadsize)
				SV_MAIN.sv_client.downloadcount = SV_MAIN.sv_client.downloadsize;
			
			if (SV_MAIN.sv_client.download == null || (name.StartsWith("maps/") && FS.file_from_pak != 0))
			{
				Com.DPrintf("Couldn't download " + name + " to " + SV_MAIN.sv_client.name + "\n");
				if (SV_MAIN.sv_client.download != null)
				{
					FS.FreeFile(SV_MAIN.sv_client.download);
					SV_MAIN.sv_client.download = null;
				}
				
				MSG.WriteByte(SV_MAIN.sv_client.netchan.message, Defines.svc_download);
				MSG.WriteShort(SV_MAIN.sv_client.netchan.message, - 1);
				MSG.WriteByte(SV_MAIN.sv_client.netchan.message, 0);
				return ;
			}
			
			SV_NextDownload_f();
			Com.DPrintf("Downloading " + name + " to " + SV_MAIN.sv_client.name + "\n");
		}
		
		//============================================================================
		
		/*
		* ================= SV_Disconnect_f
		* 
		* The client is going to disconnect, so remove the connection immediately
		* =================
		*/
		public static void  SV_Disconnect_f()
		{
			//	SV_EndRedirect ();
			SV_MAIN.SV_DropClient(SV_MAIN.sv_client);
		}
		
		/*
		* ================== SV_ShowServerinfo_f
		* 
		* Dumps the serverinfo info string ==================
		*/
		public static void  SV_ShowServerinfo_f()
		{
			Info.Print(Cvar.Serverinfo());
		}
		
		public static void  SV_Nextserver()
		{
			System.String v;
			
			//ZOID, ss_pic can be nextserver'd in coop mode
			if (SV_INIT.sv.state == Defines.ss_game || (SV_INIT.sv.state == Defines.ss_pic && 0 == Cvar.VariableValue("coop")))
				return ; // can't nextserver while playing a normal game
			
			SV_INIT.svs.spawncount++; // make sure another doesn't sneak in
			v = Cvar.VariableString("nextserver");
			//if (!v[0])
			if (v.Length == 0)
				Cbuf.AddText("killserver\n");
			else
			{
				Cbuf.AddText(v);
				Cbuf.AddText("\n");
			}
			Cvar.Set("nextserver", "");
		}
		
		/*
		* ================== SV_Nextserver_f
		* 
		* A cinematic has completed or been aborted by a client, so move to the
		* next server, ==================
		*/
		public static void  SV_Nextserver_f()
		{
			if (Lib.atoi(Cmd.Argv(1)) != SV_INIT.svs.spawncount)
			{
				Com.DPrintf("Nextserver() from wrong level, from " + SV_MAIN.sv_client.name + "\n");
				return ; // leftover from last server
			}
			
			Com.DPrintf("Nextserver() from " + SV_MAIN.sv_client.name + "\n");
			
			SV_Nextserver();
		}
		
		/*
		* ================== SV_ExecuteUserCommand ==================
		*/
		public static void  SV_ExecuteUserCommand(System.String s)
		{
			
			Com.dprintln("SV_ExecuteUserCommand:" + s);
			SV_USER.ucmd_t u = null;
			
			Cmd.TokenizeString(s.ToCharArray(), true);
			SV_USER.sv_player = SV_MAIN.sv_client.edict;
			
			//	SV_BeginRedirect (RD_CLIENT);
			
			int i = 0;
			for (; i < SV_USER.ucmds.Length; i++)
			{
				u = SV_USER.ucmds[i];
				if (Cmd.Argv(0).Equals(u.name))
				{
					u.r.Run();
					break;
				}
			}
			
			if (i == SV_USER.ucmds.Length && SV_INIT.sv.state == Defines.ss_game)
				Cmd.ClientCommand(SV_USER.sv_player);
			
			//	SV_EndRedirect ();
		}
		
		/*
		* ===========================================================================
		* 
		* USER CMD EXECUTION
		* 
		* ===========================================================================
		*/
		
		public static void  SV_ClientThink(client_t cl, usercmd_t cmd)
		{
			cl.commandMsec -= (cmd.msec & 0xFF);
			
			if (cl.commandMsec < 0 && SV_MAIN.sv_enforcetime.value_Renamed != 0)
			{
				Com.DPrintf("commandMsec underflow from " + cl.name + "\n");
				return ;
			}
			
			PlayerClient.ClientThink(cl.edict, cmd);
		}
		
		/*
		* =================== SV_ExecuteClientMessage
		* 
		* The current net_message is parsed for the given client
		* ===================
		*/
		public static void  SV_ExecuteClientMessage(client_t cl)
		{
			int c;
			System.String s;
			
			usercmd_t nullcmd = new usercmd_t();
			usercmd_t oldest = new usercmd_t(), oldcmd = new usercmd_t(), newcmd = new usercmd_t();
			int net_drop;
			int stringCmdCount;
			int checksum, calculatedChecksum;
			int checksumIndex;
			bool move_issued;
			int lastframe;
			
			SV_MAIN.sv_client = cl;
			SV_USER.sv_player = SV_MAIN.sv_client.edict;
			
			// only allow one move command
			move_issued = false;
			stringCmdCount = 0;
			
			while (true)
			{
				if (Globals.net_message.readcount > Globals.net_message.cursize)
				{
					Com.Printf("SV_ReadClientMessage: bad read:\n");
					Com.Printf(Lib.hexDump(Globals.net_message.data, 32, false));
					SV_MAIN.SV_DropClient(cl);
					return ;
				}
				
				c = MSG.ReadByte(Globals.net_message);
				if (c == - 1)
					break;
				
				switch (c)
				{
					
					default: 
						Com.Printf("SV_ReadClientMessage: unknown command char\n");
						SV_MAIN.SV_DropClient(cl);
						return ;
					
					
					
					case Defines.clc_nop: 
						break;
					
					
					case Defines.clc_userinfo: 
						cl.userinfo = MSG.ReadString(Globals.net_message);
						SV_MAIN.SV_UserinfoChanged(cl);
						break;
					
					
					case Defines.clc_move: 
						if (move_issued)
							return ; // someone is trying to cheat...
						
						move_issued = true;
						checksumIndex = Globals.net_message.readcount;
						checksum = MSG.ReadByte(Globals.net_message);
						lastframe = MSG.ReadLong(Globals.net_message);
						
						if (lastframe != cl.lastframe)
						{
							cl.lastframe = lastframe;
							if (cl.lastframe > 0)
							{
								cl.frame_latency[cl.lastframe & (Defines.LATENCY_COUNTS - 1)] = SV_INIT.svs.realtime - cl.frames[cl.lastframe & Defines.UPDATE_MASK].senttime;
							}
						}
						
						//memset (nullcmd, 0, sizeof(nullcmd));
						nullcmd = new usercmd_t();
						MSG.ReadDeltaUsercmd(Globals.net_message, nullcmd, oldest);
						MSG.ReadDeltaUsercmd(Globals.net_message, oldest, oldcmd);
						MSG.ReadDeltaUsercmd(Globals.net_message, oldcmd, newcmd);
						
						if (cl.state != Defines.cs_spawned)
						{
							cl.lastframe = - 1;
							break;
						}
						
						// if the checksum fails, ignore the rest of the packet
						
						calculatedChecksum = Com.BlockSequenceCRCByte(Globals.net_message.data, checksumIndex + 1, Globals.net_message.readcount - checksumIndex - 1, cl.netchan.incoming_sequence);
						
						if ((calculatedChecksum & 0xff) != checksum)
						{
							Com.DPrintf("Failed command checksum for " + cl.name + " (" + calculatedChecksum + " != " + checksum + ")/" + cl.netchan.incoming_sequence + "\n");
							return ;
						}
						
						if (0 == SV_MAIN.sv_paused.value_Renamed)
						{
							net_drop = cl.netchan.dropped;
							if (net_drop < 20)
							{
								
								//if (net_drop > 2)
								
								//	Com.Printf ("drop %i\n", net_drop);
								while (net_drop > 2)
								{
									SV_ClientThink(cl, cl.lastcmd);
									
									net_drop--;
								}
								if (net_drop > 1)
									SV_ClientThink(cl, oldest);
								
								if (net_drop > 0)
									SV_ClientThink(cl, oldcmd);
							}
							SV_ClientThink(cl, newcmd);
						}
						
						// copy.
						cl.lastcmd.set_Renamed(newcmd);
						break;
					
					
					case Defines.clc_stringcmd: 
						s = MSG.ReadString(Globals.net_message);
						
						// malicious users may try using too many string commands
						if (++stringCmdCount < SV_USER.MAX_STRINGCMDS)
							SV_ExecuteUserCommand(s);
						
						if (cl.state == Defines.cs_zombie)
							return ; // disconnect command
						break;
					}
			}
		}
		static SV_USER()
		{
			u1 = new ucmd_t("new", new AnonymousClassRunnable(this));
			ucmds = new ucmd_t[]{new ucmd_t("new", new AnonymousClassRunnable1(this)), new ucmd_t("configstrings", new AnonymousClassRunnable2(this)), new ucmd_t("baselines", new AnonymousClassRunnable3(this)), new ucmd_t("begin", new AnonymousClassRunnable4(this)), new ucmd_t("nextserver", new AnonymousClassRunnable5(this)), new ucmd_t("disconnect", new AnonymousClassRunnable6(this)), new ucmd_t("info", new AnonymousClassRunnable7(this)), new ucmd_t("download", new AnonymousClassRunnable8(this)), new ucmd_t("nextdl", new AnonymousClassRunnable9(this))};
		}
	}
}